package binscope

import (
	"fmt"
	"strings"

	"github.com/DangoXJ/secbinarycheck-result-analyzer/logger"
	"go.uber.org/zap"

	"github.com/360EntSecGroup-Skylar/excelize"
)

var log *zap.Logger

var passStyle = -1
var failStyle = -1

func init() {
	log = logger.GetLogger("BINSCOPE")
}

// 去掉某字符串，从右往左匹配到某关键词的部分
func fixupValue(value string, keyword string) string {
	i := strings.LastIndex(value, keyword)
	if i == -1 {
		return value
	}
	return value[i+len(keyword):]
}

// 在原始表格中根据需要添加几列，然后分析结果
func addColumns(f *excelize.File) {
	style, _ := f.GetCellStyle(DetailSheetName, "A1")
	var axis string
	for i := 0; i < len(HeadersBefore)-2; i++ {
		col := len(HeadersBefore) - i - 1
		// 插入一列
		axis = Columns[col+1] + "1"
		_ = f.InsertCol(DetailSheetName, Columns[col+1])
		_ = f.SetCellStr(DetailSheetName, axis, HeadersBefore[col]+HeaderSuffix)
		_ = f.SetCellStyle(DetailSheetName, axis, axis, style)
	}
	// 插入并处理分析结果列
	_ = f.InsertCol(DetailSheetName, Columns[HeadersAfterIndexMap[HeaderFinalResult]])
	axis = Columns[HeadersAfterIndexMap[HeaderFinalResult]] + "1"
	_ = f.SetCellStr(DetailSheetName, axis, HeaderFinalResult)
	_ = f.SetCellStyle(DetailSheetName, axis, axis, style)
	// 插入关键路径列
	_ = f.InsertCol(DetailSheetName, Columns[HeadersAfterIndexMap[HeaderKeywordPath]])
	axis = Columns[HeadersAfterIndexMap[HeaderKeywordPath]] + "1"
	_ = f.SetCellStr(DetailSheetName, axis, HeaderKeywordPath)
	_ = f.SetCellStyle(DetailSheetName, axis, axis, style)
}

// 一个一个填写分析结果
func analyse(f *excelize.File, analyzeBase map[string]map[string]string, rowNum int, row []string, compileOption string, value string, finalValue string, keyword string) string {

	columnNam := HeadersAfterIndexMap[compileOption]
	axisResult := fmt.Sprintf("%s%d", Columns[columnNam+1], rowNum+1)

	style, _ := f.GetCellStyle(DetailSheetName, fmt.Sprintf("%s%d", Columns[columnNam], rowNum+1))
	_ = f.SetCellStyle(DetailSheetName, axisResult, axisResult, style)

	// 需要分析
	if row[columnNam] == keyword {
		t := ResultToDo
		if v, ok := analyzeBase[value]; ok {

			t = strings.TrimSpace(v[compileOption])
			if t == ResultNA || t == "" {
				t = ResultToDo
			}
		}
		_ = f.SetCellStr(DetailSheetName, axisResult, t)

		finalValue = fmt.Sprintf("%s\r\n%s:\r\n%s", finalValue, compileOption, t)
		failStyle = style
	} else {
		// 无需分析
		_ = f.SetCellValue(DetailSheetName, axisResult, ResultNA)

		if row[columnNam] == ResultUnused {

		} else {
			passStyle = style
		}
	}
	return finalValue
}

// 隐藏不需要的行，无需测试
func hideUnimportantColumns(f *excelize.File) {
	_ = f.SetColVisible(DetailSheetName, Columns[HeadersAfterIndexMap[HeaderFS]], false)
	_ = f.SetColVisible(DetailSheetName, Columns[HeadersAfterIndexMap[HeaderFSAfter]], false)
	_ = f.SetColVisible(DetailSheetName, Columns[HeadersAfterIndexMap[HeaderFtrapv]], false)
	_ = f.SetColVisible(DetailSheetName, Columns[HeadersAfterIndexMap[HeaderFtrapvAfter]], false)
	_ = f.SetColVisible(DetailSheetName, Columns[HeadersAfterIndexMap[HeaderStrip]], false)
	_ = f.SetColVisible(DetailSheetName, Columns[HeadersAfterIndexMap[HeaderStripAfter]], false)
}

func analyseColumns(f *excelize.File, analyzeBase map[string]map[string]string, rowNum int, columns []string) []string {
	var ret []string

	path := columns[0]
	for _, v := range Keywords {
		path = fixupValue(path, v)
	}

	axisKeyword := fmt.Sprintf("%s%d", Columns[HeadersAfterIndexMap[HeaderKeywordPath]], rowNum+1)
	_ = f.SetCellValue(DetailSheetName, axisKeyword, path)

	finalValue := ""
	finalValue = analyse(f, analyzeBase, rowNum, columns, HeaderBindNow, path, finalValue, ResultNo)
	finalValue = analyse(f, analyzeBase, rowNum, columns, HeaderNX, path, finalValue, ResultNo)
	finalValue = analyse(f, analyzeBase, rowNum, columns, HeaderPIC, path, finalValue, ResultNo)
	finalValue = analyse(f, analyzeBase, rowNum, columns, HeaderPIE, path, finalValue, ResultNo)
	finalValue = analyse(f, analyzeBase, rowNum, columns, HeaderRELRO, path, finalValue, ResultNo)
	finalValue = analyse(f, analyzeBase, rowNum, columns, HeaderSP, path, finalValue, ResultNo)
	finalValue = analyse(f, analyzeBase, rowNum, columns, HeaderRpath, path, finalValue, ResultYes)
	finalValue = analyse(f, analyzeBase, rowNum, columns, HeaderFS, path, finalValue, ResultToDo)
	finalValue = analyse(f, analyzeBase, rowNum, columns, HeaderFtrapv, path, finalValue, ResultToDo)
	finalValue = analyse(f, analyzeBase, rowNum, columns, HeaderStrip, path, finalValue, ResultToDo)

	axisFinalResult := fmt.Sprintf("%s%d", Columns[HeadersAfterIndexMap[HeaderFinalResult]], rowNum+1)
	if finalValue != "" {
		// 存在问题，不隐藏
		_ = f.SetCellValue(DetailSheetName, axisFinalResult, finalValue)
		_ = f.SetCellStyle(DetailSheetName, axisFinalResult, axisFinalResult, failStyle)

		for j := 0; j < len(HeadersAfter); j++ {
			v, _ := f.GetCellValue(DetailSheetName, fmt.Sprintf("%s%d", Columns[j], rowNum+1))
			ret = append(ret, v)
		}
	} else {
		// 没有任何问题
		_ = f.SetCellValue(DetailSheetName, axisFinalResult, ResultNA)
		_ = f.SetCellStyle(DetailSheetName, axisFinalResult, axisFinalResult, passStyle)
		// 直接隐藏
		_ = f.SetRowVisible(DetailSheetName, rowNum+1, false)
	}

	return ret
}

// 分析所有内容
func analyseAllRows(f *excelize.File, analyzeBase map[string]map[string]string) [][]string {
	var retRows [][]string

	rows, err := f.Rows(DetailSheetName)

	if err != nil {
		log.Error("分析失败, Sheet页不存在", zap.String("sheet", DetailSheetName),
			zap.String("err", err.Error()))
		return nil
	}

	i := -1
	for rows.Next() {
		i++

		columns, _ := rows.Columns()
		// 从第二行开始分析
		if i == 0 || len(columns) == 0 {
			continue
		}
		ret := analyseColumns(f, analyzeBase, i, columns)

		if len(ret) > 0 {
			retRows = append(retRows, ret)
		}
	}
	// 打开自动筛选
	_ = f.AutoFilter(DetailSheetName, "A1", fmt.Sprintf("%s%d", Columns[HeadersAfterIndexMap[HeaderStripAfter]], i+1), "")
	return retRows
}

// BinScope 对象
type BinScope struct {
}

func NewBinScope() *BinScope {
	b := new(BinScope)
	return b
}

// 生成新的结果文件
func (b *BinScope) NewOriginalFile() *excelize.File {
	log.Info("生成汇总文件")
	f := excelize.NewFile()
	f.SetSheetName("Sheet1", DetailSheetName)
	style, _ := f.NewStyle(StyleStringHeader)
	_ = f.SetSheetRow(DetailSheetName, "A1", &HeadersBefore)
	_ = f.SetCellStyle(DetailSheetName, "A1", "L1", style)
	return f
}

// 更新结果文件
func (b *BinScope) UpdateSummaryFile(f *excelize.File, rows [][]string) {
	addColumns(f)

	stylePassed, _ := f.NewStyle(StyleStringPassed)
	styleFailed, _ := f.NewStyle(StyleStringFailed)
	styleUnused, _ := f.NewStyle(StyleStringUnused)

	m := map[string]int{}
	i := -1
	for _, row := range rows {

		if _, ok := m[row[HeadersAfterIndexMap[HeaderKeywordPath]]]; ok {
			continue
		} else {
			m[row[HeadersAfterIndexMap[HeaderKeywordPath]]] = 1
		}
		i++

		_ = f.SetSheetRow(DetailSheetName, fmt.Sprintf("A%d", i+2), &row)
		for key := range HeadersAbnormalValueMap {
			axis1 := fmt.Sprintf("%s%d", Columns[HeadersAfterIndexMap[key]], i+2)
			axis2 := fmt.Sprintf("%s%d", Columns[HeadersAfterIndexMap[key]+1], i+2)

			if row[HeadersAfterIndexMap[key]] == HeadersAbnormalValueMap[key] {
				_ = f.SetCellStyle(DetailSheetName, axis1, axis1, styleFailed)
				_ = f.SetCellStyle(DetailSheetName, axis2, axis2, styleFailed)
				// logger.Info("设置颜色", zap.String("Axis1", axis1), zap.String("Axis2", axis2), zap.Int("Style", failStyle))
			} else if row[HeadersAfterIndexMap[key]] == "UNUSED" {
				_ = f.SetCellStyle(DetailSheetName, axis1, axis1, styleUnused)
				_ = f.SetCellStyle(DetailSheetName, axis2, axis2, styleUnused)
				// logger.Info("设置颜色", zap.String("Axis1", axis1), zap.String("Axis2", axis2), zap.Int("Style", unusedStyle))

			} else {
				_ = f.SetCellStyle(DetailSheetName, axis1, axis1, stylePassed)
				_ = f.SetCellStyle(DetailSheetName, axis2, axis2, stylePassed)
				// logger.Info("设置颜色", zap.String("Axis1", axis1), zap.String("Axis2", axis2), zap.Int("Style", passStyle))

			}
		}
		axis := fmt.Sprintf("%s%d", Columns[HeadersAfterIndexMap[HeaderFinalResult]], i+2)

		if strings.Contains(row[HeadersAfterIndexMap[HeaderFinalResult]], ":\r\n"+ResultToDo) {
			_ = f.SetCellStyle(DetailSheetName, axis, axis, styleFailed)
		} else {
			_ = f.SetCellStyle(DetailSheetName, axis, axis, stylePassed)
		}

	}
	_ = f.AutoFilter(DetailSheetName, "A1", fmt.Sprintf("%s%d", Columns[HeadersAfterIndexMap[HeaderStripAfter]], len(rows)+1), "")

}

// AnalyseFromFile 分析单个文件
func (b *BinScope) AnalyseFromFile(filename string, baselines map[string]map[string]string) [][]string {
	log.Info("开始分析文件", zap.String("FileName", filename))
	f, err := excelize.OpenFile(filename)
	if err != nil {
		log.Error("打开文件失败，分析失败", zap.String("filename", filename))
		return [][]string{}
	}
	// 添加分析结果所在的各列
	addColumns(f)
	// 开始分析
	retRows := analyseAllRows(f, baselines)
	// 隐藏不需要的列
	hideUnimportantColumns(f)
	// 文档保存
	retFileName := fmt.Sprintf("%s_分析结果.xlsx", strings.ReplaceAll(filename, ".xlsx", ""))
	err = f.SaveAs(retFileName)
	if err != nil {
		log.Error("文件保存失败", zap.String("filename", retFileName))
	}
	log.Info("分析文件完成", zap.String("RetFileName", retFileName))
	return retRows
}
